Retropikzel's blog - 2025-11-06 - Making a Scheme script on unix

Did you know that UNIX scripts can be made without a shebang line? What happens then? The operating system, when it sees file marked as executable, tries to run it. If it is not executable however it will then try to run it as a shell script.[1] Why is that relevant to scheme?

In Scheme we happen to be lucky because multiline comments start with

#|

# also marks a comment in shell. So if you have file

main:

#|
    echo "Hello :3"
    exit
|#

and you mark it executable and run it:

chmod +x main
./main

it will output “Hello” and exit, never reaching line with |# which would error.

That means we can run our Scheme interpreter from there and pass it the file we are in and the given arguments. The shell script part will be ignored as it is inside the #| … |# comments.

main:

#|
    exec chibi-scheme "$0" "$@"
|#

(import (scheme base)
        (scheme write))

(display "Hello from scheme!")
(newline)

and now if you run it

./main

it will replace the shell executable with chibi-scheme, exec, and then pass it the file we are in, “$0", and all the arguments passed to the script, "$@”. You should see output “Hello from scheme!” when you run it. Notice that we dont need the exit command on the shell anymore as nothing after exec is run in the shell.

If I have understood correctly it is unclear which shell runs the script, so you should not propably use this for anything else other than scripts like this.

  1. https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01_01 “If the execl() function fails due to an error equivalent to the [ENOEXEC] error defined in the System Interfaces volume of POSIX.1-2017, the shell shall execute a command equivalent to having a shell invoked with the pathname resulting from the search as its first operand, with any remaining arguments passed to the new shell, except that the value of”$0” in the new shell may be set to the command name. If the executable file is not a text file, the shell may bypass this command execution. In this case, it shall write an error message, and shall return an exit status of 126.”